home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Unix / CNews / Source / rna / readnews.c < prev   
Encoding:
C/C++ Source or Header  |  1991-07-23  |  22.5 KB  |  1,184 lines

  1. /*
  2.  * readnews
  3.  *
  4.  *    Michael Rourke (UNSW) April 1984
  5.  */
  6.  
  7. #include "defs.h"
  8.  
  9. #define ARTSEP "/"
  10.  
  11. char admsub[BUFLEN]     = "general";
  12. char dfltsub[BUFLEN]     = "general";
  13. char mailvia[BUFLEN]     = "";
  14. char *mailpath     = MAIL;
  15.  
  16. #define    MAXARGV    10        /* used in building argv[]s below */
  17.  
  18. bool iflag;        /* -i ignore .newsrc */
  19. bool lflag;        /* -l print headers only */
  20. bool cflag;        /* -c check for news only */
  21. bool pflag;        /* -p print everything selected */
  22. bool Cflag;        /* -C verbose -c */
  23. bool sflag;        /* -s print newsgroup subscription list */
  24. bool splus;        /* -s+ */
  25. bool slistall;        /* -s? */
  26. bool sminus;        /* -s- */
  27. char *sarg;        /* arg to -s[+-] */
  28. char *nflag;        /* -n newsgroups */
  29. extern char *rcgrps;    /* -n newsgroups from newsrc file */
  30. bool n_on_cline;    /* nflag set on command line */
  31.  
  32. extern newsrc    *rc;        /* internal .newsrc */
  33.  
  34. active *alist;        /* internal active list */
  35.  
  36. #if MANGRPS
  37. char *mangrps;    /* mandatory subsciption list */
  38. #endif
  39.  
  40. long now;        /* current time */
  41. bool interrupt;        /* if interrupt hit */
  42. char *newsdir;        /* %news */
  43. bool su;        /* if super user (not used) */
  44.  
  45. applycom list(), check(), commands();
  46. void *onintr();
  47. bool ureject(), seen(), subs(), subsub();
  48.  
  49. #if MANGRPS
  50. char *getmangrps();
  51. #endif
  52.  
  53. char *progname;
  54.  
  55. main(argc, argv)
  56. int argc;
  57. char *argv[];
  58. {
  59.     char buf[BUFSIZ], *p;
  60.  
  61.     progname = argv[0];
  62.     setbuf(stdout, buf);
  63.     if (options(--argc, ++argv, true)) {
  64.         (void) fprintf(stderr, "Usage: readnews [-n newsgroups] [-i] [-clpC] [-s[-+? [group]]]\n");
  65.         exit(1);
  66.     }
  67.     now = time(&now);
  68.  
  69.     newsdir = newstr(fullartfile((char *)NULL));
  70.     getctl();
  71.  
  72.     if (!iflag)
  73.         readnewsrc();
  74.  
  75.     if (nflag)
  76.         convgrps(nflag);
  77.     else
  78.         nflag = dfltsub;
  79.     if (rcgrps)
  80.         convgrps(rcgrps);
  81.     if (!n_on_cline) {
  82. #if MANGRPS
  83.         int addsub();
  84.  
  85.         if (mangrps = getmangrps(pe.pw_cmask))
  86.             applyng(mangrps, addsub, &nflag);
  87. #endif
  88.         if (!ngmatch(admsub, nflag))
  89.             nflag = newstr3(admsub, NGSEPS, nflag);
  90.     }
  91.     if ((int) sflag + (int) lflag + (int) cflag + (int) pflag > 1)
  92.         error("-clpsC flags are mutually exclusive.");
  93.  
  94.     /* user has private mailer? */
  95.     if ((p = getenv("MAILER")) != NULL)
  96.         mailpath = newstr(p);
  97.  
  98.     alist = readactive();
  99.  
  100.     if (sflag) {
  101.         if (subs() && !iflag)
  102.             writenewsrc(alist);
  103.     } else if (lflag)
  104.         apply(alist, nflag, list, false);
  105.     else if (cflag)
  106.         apply(alist, nflag, check, false);
  107.     else {
  108.         if (!pflag) {
  109.             if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  110.                 (void) signal(SIGINT, onintr);
  111.             if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
  112.                 (void) signal(SIGQUIT, onintr);
  113.         }
  114.         apply(alist, nflag, commands, true);
  115.         if (!iflag)
  116.             writenewsrc(alist);
  117.     }
  118.     fflush(stdout);
  119.     exit(0);
  120. }
  121.  
  122. #if MANGRPS
  123. /*
  124.  * make a subscription list of all class groups the user belongs too
  125.  * (mandatory subscription)
  126.  */
  127. char *
  128. getmangrps(cmask)
  129. char *cmask;
  130. {
  131.     static char *weekday[] = { 
  132.         "mon", "tue", "wed", "thu", "fri"     };
  133.     register char **classes;
  134.     register char *s, *end;
  135.     register char *grp;
  136.     register int i, size;
  137.     extern char **getclasses();
  138.  
  139.     grp = NIL(char);
  140.     if ((classes = getclasses(cmask)) == NIL(char *))
  141.         error("Can't get classes.");
  142.     while (*classes) {
  143.         if (isdigit(**classes)) {
  144.             /*
  145.              * trim string after numeric class
  146.              * if it is a day of the week
  147.              */
  148.             s = *classes;
  149.             while (isdigit(*s) || *s == '.')
  150.                 s++;
  151.             if (*s) {
  152.                 end = s;
  153.                 while (isalpha(*end))
  154.                     end++;
  155.                 if (*end && end != s && end - s <= 3) {
  156.                     size = end - s;
  157.                     for (i = 0; i < 5; i++)
  158.                         if (CMPN(s, weekday[i], size) == 0)
  159.                             break;
  160.                     if (i != 5)
  161.                         *s = '\0';
  162.                 }
  163.             }
  164.         }
  165.         grp = (grp? catstr2(grp, ",class.", *classes):
  166.             newstr2("class.", *classes));
  167.         classes++;
  168.     }
  169.     return grp;
  170. }
  171.  
  172. /*
  173.  * if newsgroup "ng" isn't subscribed to, add it to subscription list
  174.  */
  175. addsub(ng, slist)
  176. char *ng;
  177. char **slist;
  178. {
  179.     if (!ngmatch(ng, *slist))
  180.         *slist = newstr3(ng, NGSEPS, *slist);
  181. }
  182.  
  183. #endif
  184.  
  185. void *
  186. onintr()
  187. {
  188.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  189.         (void) signal(SIGINT, onintr);
  190.     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
  191.         (void) signal(SIGQUIT, onintr);
  192.     interrupt = true;
  193. }
  194.  
  195. /*
  196.  * process options
  197.  * can be called from readnewsrc()
  198.  */
  199. options(argc, argv, cline)
  200. int argc;
  201. char *argv[];
  202. bool cline;
  203. {
  204.     register char c;
  205.  
  206.     /* TODO: use getopt(3) */
  207.     while (argc > 0) {
  208.         if (argv[0][0] != '-')
  209.             break;
  210.         while (c = *(++(argv[0]))) {
  211.             switch (c) {
  212.             case 'n':
  213.                 if (cline)
  214.                     nflag = argv[1], n_on_cline = true;
  215.                 else {
  216.                     if (!n_on_cline)
  217.                         nflag = (nflag?
  218.                             catstr2(nflag, NGSEPS, argv[1]):
  219.                             newstr(argv[1]));
  220.                     rcgrps = (rcgrps?
  221.                         catstr2(rcgrps, NGSEPS, argv[1]):
  222.                         newstr(argv[1]));
  223.                 }
  224.                 argc--, argv++; 
  225.                 break;
  226.             case 'i':
  227.                 iflag = true; 
  228.                 continue;
  229.             case 's':
  230.                 sflag = true;
  231.                 switch (argv[0][1]) {
  232.                 case '\0':
  233.                     continue;
  234.                 case '+':
  235.                     splus = true; 
  236.                     break;
  237.                 case '?':
  238.                     slistall = true, ++(argv[0]); 
  239.                     continue;
  240.                 case '-':
  241.                     sminus = true; 
  242.                     break;
  243.                 default:
  244.                     argc = -1; 
  245.                     break;
  246.                 }
  247.                 if (argc > 0) {
  248.                     sarg = newstr(argv[1]);
  249.                     argc--, argv++;
  250.                 }
  251.                 break;
  252.             case 'p':
  253.                 pflag = true; 
  254.                 continue;
  255.             case 'l':
  256.                 lflag = true; 
  257.                 continue;
  258.             case 'c':
  259.                 cflag = true; 
  260.                 continue;
  261.             case 'C':
  262.                 cflag = Cflag = true; 
  263.                 continue;
  264.             default:
  265.                 argc = -1; 
  266.                 break;
  267.             }
  268.             break;
  269.         }
  270.         argc--, argv++;
  271.     }
  272.     return argc != 0;
  273. }
  274.  
  275. /*
  276.  * subscription list handling
  277.  * return true if newsrc is to be re-written
  278.  */
  279. bool
  280. subs()
  281. {
  282.     register newsrc    *np;
  283.     register active    *ap;
  284.     register char *tmp, *com;
  285.     register FILE *f;
  286.  
  287.     if (slistall) {
  288.         (void) printf("Active newsgroups:\n");
  289.         (void) fflush(stdout);
  290.         /* possibly vestigial code here, old #ifdefs deleted */
  291.         f = stdout;
  292.         com = 0;        /* for lint */
  293.         com = com;        /* for lint */
  294.         for (ap = alist; ap; ap = ap->a_next)
  295.             (void) fprintf(f, "%s\n", ap->a_name);
  296.         return false;
  297.     } else if (splus || sminus) {
  298.         if (strpbrk(sarg, BADGRPCHARS)) {
  299.             (void) printf("%s: Illegal char in newsgroup.\n", sarg);
  300.             return false;
  301.         }
  302.         if (ngmatch(sarg, nflag)) {
  303.             /*
  304.              * normally we subscribe, check for an exclusion
  305.              */
  306.             for (np = rc; np; np = np->n_next)
  307.                 if (CMP(sarg, np->n_name) == 0)
  308.                     break;
  309.             if (np) {
  310.                 /*
  311.                  * altering subscribe flag is all
  312.                  * we need to change
  313.                  */
  314.                 np->n_subscribe = splus;
  315.                 return true;
  316.             }
  317.             if (sminus) {
  318.                 /*
  319.                  * try deleting from sub list
  320.                  */
  321.                 if (subsub(sarg, rcgrps))
  322.                     return true;
  323.                 /*
  324.                  * add specific exclusion
  325.                  */
  326.                 rcgrps = newstr4(rcgrps, NGSEPS, NEGS, sarg);
  327.                 return true;
  328.             }
  329.         } else if (splus) {
  330.             /*
  331.              * we don't subscribe,
  332.              * try deleting !sarg first
  333.              */
  334.             tmp = newstr2(NEGS, sarg);
  335.             subsub(tmp, rcgrps);
  336.             if (!ngmatch(sarg, rcgrps))
  337.                 /*
  338.                  * didn't work, so add explicit subscription
  339.                  */
  340.                 rcgrps = rcgrps? newstr3(rcgrps, NGSEPS, sarg):
  341.                     newstr(sarg);
  342.             return true;
  343.         }
  344.     } else {
  345.         (void) printf("Subscription list: %s", nflag);
  346.         for (np = rc; np; np = np->n_next)
  347.             if (!np->n_subscribe && ngmatch(np->n_name, nflag))
  348.                 (void) printf(",!%s", np->n_name);
  349.         (void) printf("\n");
  350.     }
  351.     return false;
  352. }
  353.  
  354.  
  355. /*
  356.  * try and delete group from subscription list
  357.  * return true if successful
  358.  */
  359. bool
  360. subsub(grp, slist)
  361. char *grp;
  362. char *slist;
  363. {
  364.     register char *delim;
  365.  
  366.     while (*slist) {
  367.         if (delim = strchr(slist, NGSEPCHAR))
  368.             *delim = '\0';
  369.         if (CMP(grp, slist) == 0) {
  370.             if (delim)
  371.                 (void) strcpy(slist, delim + 1);
  372.             else if (slist[-1] == ',')
  373.                 slist[-1] = '\0';
  374.             else
  375.                 slist[0] = '\0';
  376.             return true;
  377.         }
  378.         if (delim)
  379.             *delim = NGSEPCHAR, slist = delim + 1;
  380.         else
  381.             break;
  382.     }
  383.     return false;
  384. }
  385.  
  386. char *
  387. ltoa(l)
  388. long l;
  389. {
  390.     static char buf[30];
  391.  
  392.     sprintf(buf, "%ld", l);
  393.     return buf;
  394. }
  395.  
  396. /*
  397.  * list titles command (-l)
  398.  */
  399. applycom
  400. list(ap, np)
  401. active *ap;
  402. newsrc *np;
  403. {
  404.     static active *lastap;
  405.     static bool first = true;
  406.     register char *fname;
  407.     register FILE *f;
  408.     header h;
  409.     ino_t ino;
  410.  
  411.     np->n_last++;
  412.     fname = convg(newstr5(newsdir, "/", ap->a_name, ARTSEP,
  413.         ltoa(np->n_last)));
  414.     ino = 0;
  415.     f = fopen(fname, "r");
  416.     free(fname);
  417.     if (!f || seen(f, &ino))
  418.         return next;
  419.     gethead(f, &h);
  420.     if (first) {
  421.         (void) printf("News articles:\n");
  422.         first = false;
  423.     }
  424.     if (lastap != ap)
  425.         (void) printf("  %s:\n", ap->a_name);
  426.     lastap = ap;
  427.     (void) printf("    %-4d %s\n", np->n_last, h.h_subject);
  428.     (void) fclose(f);
  429.     freehead(&h);
  430.     if (ino)
  431.         seen(NIL(FILE), &ino);
  432.     return next;
  433. }
  434.  
  435. /*
  436.  * check command (-c or -C)
  437.  */
  438. applycom
  439. check(ap, np)
  440. active *ap;
  441. newsrc *np;
  442. {
  443.     static bool done;
  444.  
  445.     np->n_last++;
  446.     if (Cflag) {
  447.         register long num;
  448.  
  449.         if (!done)
  450.             (void) printf("You have news:\n");
  451.         done = true;
  452.         num = ap->a_seq - np->n_last + 1;
  453.         (void) printf("\t%s at most %ld article%s\n",
  454.             ap->a_name, num, (num > 1? "s": ""));
  455.         return nextgroup;
  456.     } else {
  457.         (void) printf("You have news.\n");
  458.         fflush(stdout);
  459.         exit(0);
  460.         /* NOTREACHED */
  461.     }
  462. }
  463.  
  464. /*
  465.  * normal command handler (or pflag)
  466.  * commands:
  467.  *
  468.  * \n         print current article
  469.  * n         go to next article
  470.  * q        quit
  471.  * r        reply
  472.  * f         followup
  473.  * p         postnews
  474.  * N [newsgrp]    next newsgroup
  475.  * s [file]    save
  476.  * U        unsubscribe from group
  477.  * !stuff    shell escape
  478.  * number or .    go to number
  479.  * -         back to previous article (toggle)
  480.  * x        quick exit
  481.  * h        long header info
  482.  * H        full header
  483.  *
  484.  * inside r, f or p:
  485.  *    .e    edit
  486.  *    .i    interpolate
  487.  *    . or EOT terminate message
  488.  *    .!comd    shell escape
  489.  */
  490. applycom
  491. commands(ap, np, last, pushed)
  492. active *ap;
  493. newsrc *np;
  494. bool last;
  495. bool pushed;
  496. {
  497.     register char *com, *arg;
  498.     register int c, size;
  499.     register long i;
  500.     register FILE *f;
  501.     char *fname;
  502.     header        h;
  503.     newsrc        ntmp;
  504.     ino_t        ino;
  505.     bool printed, pheader, verbose, hadinterrupt;
  506.     applycom    nextact;
  507.     static char errmess[] = "Unknown command; type `?' for help.\n";
  508.     static char form[]    = "%s: %s\n";
  509.     static char savedsys[BUFSIZ / 2];
  510.     static active    *lastap, *rlastap;
  511.     static newsrc    lastn;
  512.     static char number[20];
  513.     static active    *wantap;
  514.     extern char t_from[], t_subject[], t_date[];
  515.     extern char t_newsgroups[], t_path[], t_sender[];
  516.     extern char t_replyto[], t_organization[];
  517.     extern active    *activep();
  518.  
  519.     if (last) {
  520.         /*
  521.          * give user one last chance to
  522.          * see this article
  523.          */
  524.         ap = rlastap;
  525.         np = &lastn;
  526.         wantap = NIL(active);
  527.         if (!ap || pflag)
  528.             return stop;
  529.     } else if (wantap)
  530.         /*
  531.          * doing an "n newsgroup" command
  532.          */
  533.         if (wantap != ap)
  534.             return nextgroup;
  535.         else
  536.             wantap = NULL;
  537.  
  538.     fname = convg(newstr5(newsdir, "/", ap->a_name, ARTSEP,
  539.         ltoa(np->n_last + 1)));
  540.     f = fopen(fname, "r");
  541.     ino = 0;
  542.     if (!f || !last && !pushed && seen(f, &ino)) {
  543.         if (pushed)
  544.             (void) printf("Article %ld (%s) no longer exists.\n",
  545.                 np->n_last + 1, ap->a_name);
  546.         else
  547.             np->n_last++;
  548.         if (f)
  549.             (void) fclose(f);
  550.         free(fname);
  551.         return next;
  552.     }
  553.  
  554.     gethead(f, &h);
  555.  
  556.     (void) printf("\n");
  557.     interrupt = hadinterrupt = verbose = false;
  558.     if (last) {
  559.         (void) printf("No more articles (press RETURN again to quit).\n");
  560.         printed = pheader = true;
  561.     } else
  562.         printed = pheader = false;
  563.  
  564.     while (1) {
  565.         if (lastap != ap) {
  566.             size = strlen(ap->a_name) + sizeof("Newsgroup");
  567.             for (i = 0; i < size; i++)
  568.                 (void) putc('-', stdout);
  569.             (void) printf("\nNewsgroup %s\n", ap->a_name);
  570.             for (i = 0; i < size; i++)
  571.                 (void) putc('-', stdout);
  572.             (void) printf("\n\n");
  573.         }
  574.         lastap = ap;
  575.         if (!pheader) {
  576.             time_t itsdate;
  577.             (void) printf("Article %ld of %ld (%s)",
  578.                 np->n_last + 1, ap->a_seq, ap->a_name);
  579.             if (h.h_lines != 0)
  580.                 (void) printf(" (%s lines)", h.h_lines);
  581.             if (h.h_date != NULL) {
  582.                 itsdate = atot(h.h_date);
  583.                 (void) printf(" %s", ctime(&itsdate));
  584.             } else
  585.                 (void) printf(" %s", "<no date!>\n");
  586.             (void) printf(form, t_subject, h.h_subject);
  587.             (void) printf(form, t_from, h.h_from);
  588.             if (verbose || pflag) {
  589.                 (void) printf(form, t_date, h.h_date);
  590.                 (void) printf(form, t_newsgroups, h.h_newsgroups);
  591.                 (void) printf(form, t_path, h.h_path);
  592.                 if (h.h_sender)
  593.                     (void) printf(form, t_sender, h.h_sender);
  594.                 if (h.h_replyto)
  595.                     (void) printf(form, t_replyto, h.h_replyto);
  596.                 if (h.h_organisation)
  597.                     (void) printf(form, t_organization, h.h_organisation);
  598.                 verbose = false;
  599.             }
  600.             pheader = true;
  601.         }
  602.         if (!pushed && number[0])
  603.             /*
  604.              * just returned from a number command
  605.              * and have another to do
  606.              */
  607.             com = number;
  608.         else if (pflag)
  609.             /*
  610.              * just print it
  611.              */
  612.             com = "";
  613.         else {
  614.             (void) printf("? ");
  615.             if (fflush(stdout) == EOF) {
  616.                 (void) printf("\n? ");
  617.                 (void) fflush(stdout);
  618.             }
  619.             interrupt = false;
  620.             if ((com = mgets()) == NIL(char)) {
  621.                 if (interrupt)
  622.                     if (!hadinterrupt) {
  623.                         clearerr(stdin);
  624.                         (void) printf("Interrupt\n");
  625.                         hadinterrupt = true;
  626.                         interrupt = false;
  627.                         continue;
  628.                     }
  629.                     else
  630.                         exit(1);
  631.                 nextact = stop;
  632.                 break;
  633.             }
  634.             hadinterrupt = false;
  635.         }
  636.         if (*com == '!') {
  637.             if (com[1] == '!') {
  638.                 (void) printf("!%s\n", savedsys);
  639.                 com = savedsys;
  640.             } else
  641.                 com++;
  642.             (void) fflush(stdout);
  643. #ifdef F_SETFD
  644.             (void) fcntl(fileno(f), F_SETFD, 1);    /* close on exec */
  645. #endif
  646.             (void) system(com);
  647.             if (com != savedsys)
  648.                 strncpy(savedsys, com, sizeof(savedsys) - 1);
  649.             (void) printf("!\n");
  650.             if (!printed)
  651.                 pheader = false;
  652.             continue;
  653.         }
  654.         /*
  655.          * check command syntax
  656.          */
  657.         if (*com && !isdigit(*com) && com[1] && (!isspace(com[1]) ||
  658.             strchr("Nsm", *com) == NULL)) {
  659.             (void) printf(errmess);
  660.             continue;
  661.         }
  662.         if (c = *com) {
  663.             arg = com;
  664.             while (isspace(*++arg))
  665.                 ;
  666.         } else
  667.             arg = NULL;
  668.         switch (c) {
  669.         case 0:
  670.         case '.':
  671.             if (!printed || c == '.') {
  672.                 if (pflag)
  673.                     (void) printf("\n");
  674.                 print(&h, f);
  675.                 if (pflag) {
  676.                     nextact = next;
  677.                     break;
  678.                 }
  679.                 printed = true;
  680.                 continue;
  681.             }
  682.         case 'n':            /* B compatible */
  683.         case '+':
  684.         case ';':
  685.             nextact = next;
  686.             break;
  687.         case '?':
  688.             help();
  689.             continue;
  690.         case 'r':
  691.             reply(&h, fname);
  692.             continue;
  693.         case 'f':
  694.             followup(&h, fname);
  695.             continue;
  696.         case 'p':
  697.             pnews(ap->a_name);
  698.             continue;
  699.         case 'U':
  700.             if (ngmatch(np->n_name, admsub)
  701. #if MANGRPS
  702.              || ngmatch(np->n_name, mangrps))
  703. #else
  704.                 )
  705. #endif
  706.                  {
  707.                     (void) printf(
  708.                     "Group \"%s\" can't be unsubscribed.\n",
  709.                         np->n_name);
  710.                     continue;
  711.                 }
  712.             np->n_subscribe = false;
  713.             nextact = nextgroup;
  714.             break;
  715.         case 'N':            /* B compatible */
  716.             if (!*arg) {
  717.                 nextact = nextgroup;
  718.                 break;
  719.             }
  720.             if ((wantap = activep(arg)) == NIL(active)) {
  721.                 (void) printf("%s: non-existent newsgroup.\n", arg);
  722.                 continue;
  723.             }
  724.             if (!ngmatch(arg, nflag)) {
  725.                 (void) printf("%s: is not subscribed to!\n", arg);
  726.                 wantap = NULL;
  727.                 continue;
  728.             }
  729.             nextact = searchgroup;
  730.             break;
  731.         case 's':
  732.             save(&h, f, arg);
  733.             continue;
  734.         case 'q':
  735.             nextact = stop;
  736.             break;
  737.         case 'x':
  738.             fflush(stdout);
  739.             exit(0);
  740.         case 'h':
  741.             verbose = true;
  742.             pheader = false;
  743.             continue;
  744.         case 'H':
  745.             puthead(&h, stdout, printing);
  746.             continue;
  747.         case '-':
  748.             if (pushed) {
  749.                 nextact = next;
  750.                 break;
  751.             }
  752.             if (!rlastap || !lastn.n_name) {
  753.                 (void) printf("Can't go back!\n");
  754.                 continue;
  755.             }
  756.             nextact = commands(rlastap, &lastn, false, true);
  757.             /*
  758.              * number commands, after a "-" act on the
  759.              * group of the "-" command
  760.              */
  761.             while (number[0]) {
  762.                 ntmp = lastn;
  763.                 ntmp.n_last = atol(number) - 1;
  764.                 number[0] = '\0';
  765.                 nextact = commands(rlastap, &ntmp, false, true);
  766.             }
  767.             if (nextact != next)
  768.                 break;
  769.             (void) printf("\n");
  770.             pheader = false;
  771.             continue;
  772.         default:
  773.             if (isdigit(c)) {
  774. /*                i = atol(arg);        */
  775.                 i = c - '0';
  776.                 while (isdigit(*arg))
  777.                     i = i * 10 + *arg++ - '0';
  778.             }
  779.             if (!isdigit(c) || *arg != '\0') {
  780.                 (void) printf(errmess);
  781.                 continue;
  782.             }
  783.             number[0] = '\0';
  784.             if (i < ap->a_low || i > ap->a_seq) {
  785.                 (void) printf(
  786.                     "Articles in \"%s\" group range %ld to %ld.\n",
  787.                     np->n_name, ap->a_low, ap->a_seq);
  788.                 continue;
  789.             }
  790.             if (pushed) {
  791.                 sprintf(number, "%ld", i);
  792.                 nextact = next;
  793.                 break;
  794.             }
  795.             ntmp = *np;
  796.             ntmp.n_last = i - 1;
  797.             if ((nextact = commands(ap, &ntmp, false, true)) != next)
  798.                 break;
  799.             if (!number[0]) {
  800.                 (void) printf("\n");
  801.                 pheader = false;
  802.             }
  803.             continue;
  804.         }
  805.         break;
  806.     }
  807.     rlastap = ap;
  808.     lastn = *np;
  809.     if (!pushed && (nextact == next || printed)) {
  810.         np->n_last++;
  811.         if (ino)
  812.             seen(NIL(FILE), &ino);
  813.     }
  814.     freehead(&h);
  815.     (void) fclose(f);
  816.     free(fname);
  817.     return nextact;
  818. }
  819.  
  820.  
  821. /*
  822.  * see if the article has links, if so have we seen it?
  823.  * close file if we return true
  824.  *
  825.  * called twice,
  826.  *    first (with f set) to test (and possibly set *ino)
  827.  *    again to put *ino in memory
  828.  */
  829. bool
  830. seen(f, ino)
  831. FILE *f;
  832. ino_t *ino;
  833. {
  834.     static int num;
  835.     static ino_t    *ilist;
  836.     struct stat statb;
  837.     register int i;
  838.  
  839.     if (f) {
  840.         if (fstat(fileno(f), &statb) != 0 || statb.st_nlink <= 1)
  841.             return false;
  842.         for (i = 0; i < num; i++)
  843.             if (ilist[i] == statb.st_ino) {
  844.                 (void) fclose(f);
  845.                 return true;
  846.             }
  847.         *ino = statb.st_ino;
  848.         return false;
  849.     } else if (*ino) {
  850.         num++;
  851.         ilist = (ino_t * ) (ilist ? myrealloc((char *) ilist, (int) sizeof(ino_t) *
  852.             num) : myalloc((int) sizeof(ino_t)));
  853.         ilist[num - 1] = *ino;
  854.     }
  855.     return true;
  856. }
  857.  
  858.  
  859. /*
  860.  * print out help file
  861.  */
  862. help()
  863. {
  864.     register FILE    *f;
  865.     register int c;
  866.     register char *helppath;
  867.  
  868.     helppath = ctlfile("readnews.help");
  869.     if ((f = fopen(helppath, "r")) == NIL(FILE)) {
  870.         (void) printf("Can't open %s\n", helppath);
  871.         return;
  872.     }
  873.     while ((c = getc(f)) != EOF)
  874.         (void) putc(c, stdout);
  875.     (void) fclose(f);
  876. }
  877.  
  878. /*
  879.  * reply to sender by mail
  880.  */
  881. /* ARGSUSED fname */
  882. reply(hp, fname)
  883. header *hp;
  884. char *fname;
  885. {
  886.     char *argv[MAXARGV];
  887.     register int argc;
  888.  
  889.     argc = 0;
  890.     argv[argc++] = "mail";
  891. #ifdef UNSWMAIL
  892.     argv[argc++] = "-s";
  893.     if ((argv[argc++] = getsubject(hp)) == NIL(char))
  894.         return;
  895.     argv[argc++] = "-i";
  896.     argv[argc++] = fname;
  897. #endif
  898.  
  899.     if ((argv[argc++] = getretaddr(hp)) == NIL(char)) {
  900.         (void) printf("Can't work out an address!\n");
  901.         return;
  902.     }
  903.  
  904.     argv[argc++] = NIL(char);
  905.  
  906.     run(mailpath, argv, false);
  907.  
  908.     free(argv[argc - 2]);
  909. }
  910.  
  911.  
  912.  
  913. /*
  914.  * generate correct headers for a followup article
  915.  * then call postnews.
  916.  */
  917. followup(hp, fname)
  918. header *hp;
  919. char *fname;
  920. {
  921.     char tmpf[50];
  922.     register FILE *fo;
  923.     char *s = getsubject(hp);
  924.  
  925.     if (s == NULL)
  926.         return;
  927.     (void) strcpy(tmpf, "/tmp/rfXXXXXX");
  928.     (void) mktemp(tmpf);
  929.     fo = fopen(tmpf, "w");
  930.     if (fo == NULL)
  931.         error("can't create `%s'", tmpf);
  932.     fprintf(fo, "Newsgroups: %s\n", (hp->h_followupto) ? hp->h_followupto :
  933.                             hp->h_newsgroups);
  934.     fprintf(fo, "Subject: %s\n", s);
  935.     free(s);
  936.     if (hp->h_references && hp->h_messageid)
  937.         fprintf(fo, "References: %s %s\n", hp->h_references, hp->h_messageid);
  938.     else if (hp->h_messageid)
  939.         fprintf(fo, "References: %s\n", hp->h_messageid);
  940.     (void) fclose(fo);
  941.  
  942.     s = newstr3(binfile("inject/postnews"), " -h ", tmpf);
  943.     system(s);
  944.     free(s);
  945.  
  946.     (void) unlink(tmpf);
  947. }
  948.  
  949. /*
  950.  * get correct "Subject: Re: .." line
  951.  */
  952. char *
  953. getsubject(hp)
  954. register header *hp;
  955. {
  956.     register char *s;
  957.  
  958.     if (!hp->h_subject) {
  959.         (void) printf("Subject: Re: ");
  960.         (void) fflush(stdout);
  961.         if ((s = mgets()) == NIL(char) || !*s) {
  962.             (void) printf("The Subject field is mandatory.\n");
  963.             return NIL(char);
  964.         }
  965.         return newstr2("Re: ", s);
  966.     } else if (CMPN(hp->h_subject, "Re: ", 4) != 0 && CMPN(hp->h_subject,
  967.          "re: ", 4) != 0)
  968.         return newstr2("Re: ", hp->h_subject);
  969.     else
  970.         return newstr(hp->h_subject);
  971. }
  972.  
  973.  
  974. /*
  975.  * run a command, optionally closing stdin
  976.  */
  977. run(com, argv, closein)
  978. char *com;
  979. char *argv[];
  980. bool closein;
  981. {
  982.     int pid, status, r;
  983.  
  984.     switch (pid = fork()) {
  985.     default:
  986.         /* parent */
  987.         break;
  988.     case 0:
  989.         /* child */
  990.         if (closein)
  991.             close(fileno(stdin));
  992.         execvp(com, argv);
  993.         error("can't exec %s", com);
  994.         exit(1);
  995.  
  996.     case -1:
  997.         error("can't fork");
  998.     }
  999.  
  1000.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  1001.         (void) signal(SIGINT, SIG_IGN);
  1002.     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
  1003.         (void) signal(SIGQUIT, SIG_IGN);
  1004.  
  1005.     while ((r = wait(&status)) != pid && r != -1)
  1006.         ;
  1007.  
  1008.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  1009.         (void) signal(SIGINT, onintr);
  1010.     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
  1011.         (void) signal(SIGQUIT, onintr);
  1012. }
  1013.  
  1014. /*
  1015.  * call postnews
  1016.  */
  1017. pnews(group)
  1018. char *group;
  1019. {
  1020.     register char *s = newstr3(binfile("inject/postnews"), " ", group);
  1021.     system(s);
  1022.     free(s);
  1023. }
  1024.  
  1025. /*
  1026.  * save an article
  1027.  */
  1028. save(hp, f, s)
  1029. header *hp;
  1030. FILE *f;
  1031. char *s;
  1032. {
  1033.     register long pos;
  1034.     register int c;
  1035.     register char *cp;
  1036.     register FILE    *sf;
  1037.     register char *aname;
  1038.     long then;
  1039.     extern char *getenv();
  1040.  
  1041.     if (!*s) {
  1042.         if ((aname = getenv("HOME")) == NIL(char)) {
  1043.             (void) printf("No $HOME in environment.\n");
  1044.             return;
  1045.         }
  1046.         s = aname = newstr3(aname, "/", ARTICLES);
  1047.     } else
  1048.         aname = NIL(char);
  1049.     if ((sf = fopen(s, "a")) == NIL(FILE)) {
  1050.         (void) fprintf(stderr, "readnews: can't open %s\n", s);
  1051.         return;
  1052.     }
  1053.     if (aname)
  1054.         free(aname);
  1055.     pos = ftell(f);
  1056.     rewind(f);
  1057.     if (cp = strchr(hp->h_from, ' '))
  1058.         *cp = '\0';
  1059.     if (hp->h_date)
  1060.         then = atot(hp->h_date);
  1061.     else
  1062.         then = 0L;
  1063.     (void) fprintf(sf, "From %s %s", hp->h_from, ctime(then ? &then : &now));
  1064.     if (cp)
  1065.         *cp = ' ';
  1066.     while ((c = getc(f)) != EOF)
  1067.         (void) putc(c, sf);
  1068.     (void) putc('\n', sf);
  1069.     (void) fclose(sf);
  1070.     fseek(f, pos, 0);
  1071. }
  1072.  
  1073.  
  1074.  
  1075. /*
  1076.  * print an article, if it's long enough call page()
  1077.  */
  1078. /* ARGSUSED */
  1079. print(hp, f)
  1080. header *hp;
  1081. FILE *f;
  1082. {
  1083.     register int c;
  1084.     register long pos;
  1085.  
  1086.     pos = ftell(f);
  1087.     if (!pflag)
  1088.         page(f);
  1089.     else
  1090.         while ((c = getc(f)) != EOF)
  1091.             (void) putchar(c);
  1092.     (void) fseek(f, pos, 0);
  1093. }
  1094.  
  1095.  
  1096. /*
  1097.  * copy article text to stdout, and break into pages
  1098.  */
  1099. page(f)
  1100. FILE *f;
  1101. {
  1102.     register int c;
  1103.     register unsigned lineno;
  1104.     char lbuf[80];
  1105.  
  1106.     lineno = 1;
  1107.     while (!interrupt) {
  1108.         for (; lineno < PAGESIZE - 4 && !interrupt; lineno++) {
  1109.             while ((c = getc(f)) != EOF && c != '\n')
  1110.                 (void) putchar(c);
  1111.             if (c == EOF)
  1112.                 goto fastexit;
  1113.             if (lineno < PAGESIZE - 5)
  1114.                 (void) putchar('\n');
  1115.         }
  1116.         if (interrupt)
  1117.             break;
  1118.         if (fflush(stdout) == EOF)
  1119.             break;
  1120.         if (read(fileno(stdin), lbuf, sizeof(lbuf)) <= 0)
  1121.             break;
  1122.         lineno = 0;
  1123.     }
  1124.     if (lineno)
  1125.         (void) putchar('\n');
  1126.     interrupt = false;
  1127. fastexit:    ;
  1128. }
  1129.  
  1130. /* VARARGS1 */
  1131. error(s, a0, a1, a2, a3)
  1132. char *s;
  1133. {
  1134.     (void) fprintf(stderr, "readnews: ");
  1135.     (void) fprintf(stderr, s, a0, a1, a2, a3);
  1136.     (void) fprintf(stderr, "\n");
  1137.     fflush(stdout);        /* just on principle */
  1138.     exit(1);
  1139. }
  1140.  
  1141. /*
  1142.  - unprivileged - no-op needed to keep the pathname stuff happy
  1143.  */
  1144. void
  1145. unprivileged(reason)
  1146. char *reason;
  1147. {
  1148. }
  1149.  
  1150. /*
  1151.  - getctl - pick up control file for subscriptions etc.
  1152.  */
  1153. getctl()
  1154. {
  1155.     register FILE *f;
  1156.     register char *fname;
  1157.     char line[BUFLEN];
  1158.     register char *p;
  1159.  
  1160.     fname = ctlfile("readnews.ctl");
  1161.     f = fopen(fname, "r");
  1162.     if (f == NULL)
  1163.         return;
  1164.  
  1165.     while (fgets(line, sizeof(line), f) != NULL) {
  1166.         line[strlen(line)-1] = '\0';    /* dispose of newline */
  1167.         p = strchr(line, '\t');
  1168.         if (p == NULL)
  1169.             p = strchr(line, ' ');
  1170.         if (line[0] != '#' && p != NULL) {
  1171.             while (*p == ' ' || *p == '\t')
  1172.                 *p++ = '\0';
  1173.             if (strcmp(line, "defsub") == 0)
  1174.                 (void) strcpy(dfltsub, p);
  1175.             else if (strcmp(line, "mustsub") == 0)
  1176.                 (void) strcpy(admsub, p);
  1177.             else if (strcmp(line, "mailvia") == 0)
  1178.                 (void) strcpy(mailvia, p);
  1179.         }
  1180.     }
  1181.  
  1182.     (void) fclose(f);
  1183. }
  1184.